using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Pinwheel.Toolkits.Generator
{
    public enum MemberKind
    {
        NonSerializable = -1, // not allowed
        BuiltIn = 0,
        Enum,
        String,
        Unmanaged,
        Struct,
        Class,
        Array,
        List,
        Dictionary,
        Interface,
    }
    
    public partial class TypeMeta
    {
        readonly ReferenceSymbols reference;
        public INamedTypeSymbol Symbol { get; set; }
        
        
        public string TypeName { get; }
        public string FullName { get; }
        
        public bool IsValueType { get; set; }
        public int HashCode { get; }
        
        public MemberMeta[] Members { get; private set; }
        
        public TypeMeta(INamedTypeSymbol symbol, ReferenceSymbols reference)
        {
            this.reference = reference;
            this.Symbol = symbol;
            this.TypeName = symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
            this.FullName = Symbol.ToDisplayString();
            this.HashCode = symbol.GetHashCode();
            this.IsValueType = symbol.IsValueType;
            
            
            this.Members = symbol.GetAllMembers() // iterate includes parent type
                .Where(x => x is (IFieldSymbol or IPropertySymbol) and { IsStatic: false, IsImplicitlyDeclared: false, CanBeReferencedByName: true })
                .Reverse()
                .DistinctBy(x => x.Name) // remove duplicate name(new)
                .Reverse()
                .Where(x =>
                {
                    var include = x.ContainsAttribute(reference.LSerializeIncludeAttribute);
                    var ignore = x.ContainsAttribute(reference.LSerializeIgnoreAttribute);
                    if (ignore) return false;
                    if (include) return true;
                    return x.DeclaredAccessibility is Accessibility.Public;
                })
                .Where(x =>
                {
                    if (x is IPropertySymbol p)
                    {
                        // set only can't be serializable member
                        if (p.GetMethod == null && p.SetMethod != null)
                        {
                            return false;
                        }
                        if (p.IsIndexer) return false;
                    }
                    return true;
                })
                .Select((x, i) => new MemberMeta(x, reference, i))
                .OrderBy(x => x.Order)
                .ToArray();
            
        }

        
        public bool Validate(TypeDeclarationSyntax syntax, IGeneratorContext context)
        {
            var noError = true;
            
            

            return noError;
        }
    }

    public partial class MemberMeta
    {
        public ISymbol Symbol { get; }
        public string Name { get; }
        public ITypeSymbol MemberType { get; }
        public int Order { get; }
        
        public MemberKind Kind { get; }
        
        public ImmutableArray<ITypeSymbol> TypeArguments { get; }
        
        public bool Nullable { get; }

        MemberMeta(int order)
        {
            this.Symbol = null!;
            this.Name = null!;
            this.MemberType = null!;
            this.Order = order;
            this.Kind = MemberKind.NonSerializable;
            this.TypeArguments = ImmutableArray<ITypeSymbol>.Empty;
        }

        public MemberMeta(ISymbol symbol, ReferenceSymbols references, int sequentialOrder)
        {
            this.Symbol = symbol;
            this.Name = symbol.Name;
            this.Order = sequentialOrder;
            if (symbol is IFieldSymbol f)
            {
                MemberType = f.Type;
            }
            else if (symbol is IPropertySymbol p)
            {
                MemberType = p.Type;
            }

            this.Nullable = IsNullable(MemberType, references);
            
            // 处理可空类型的内部类型（如 Nullable<T> 中的 T）
            if (Nullable && MemberType is INamedTypeSymbol nullableNamedType)
            {
                MemberType = nullableNamedType.TypeArguments.FirstOrDefault() ?? MemberType;
            }
            
            this.Kind = ParseMemberKind(symbol, MemberType, references);
            if (this.Kind == MemberKind.Array && MemberType is IArrayTypeSymbol array)
                this.TypeArguments = ImmutableArray.Create(array.ElementType);
            else
                this.TypeArguments = MemberType is INamedTypeSymbol namedType
                    ? namedType.TypeArguments
                    : ImmutableArray<ITypeSymbol>.Empty;
        }
        
        public static bool IsNullable(ITypeSymbol typeSymbol, ReferenceSymbols references)
        {
            // 值类型的 Nullable<T>
            if (typeSymbol is INamedTypeSymbol named
                && named.IsGenericType
                && named.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T)
            {
                return true;
            }

            // 可空引用类型（C# 8+）
            switch (typeSymbol.NullableAnnotation)
            {
                case NullableAnnotation.Annotated:
                    return true;
                default:
                    return false;
            }
        }

        static MemberKind ParseMemberKind(ISymbol memberSymbol, ITypeSymbol memberType, ReferenceSymbols references)
        {
            if (memberType.SpecialType 
                    is SpecialType.System_Object 
                    or SpecialType.System_Array
                    or SpecialType.System_Delegate 
                    or SpecialType.System_MulticastDelegate 
                || memberType.TypeKind == TypeKind.Delegate)
            {
                return MemberKind.NonSerializable; // object, Array, delegate is not allowed
            }
            else if (memberType.TypeKind == TypeKind.Enum)
            {
                return MemberKind.Enum;
            }
            else if (memberType.SpecialType == SpecialType.System_String)
            {
                return MemberKind.String;
            }
            else if (memberType.IsUnmanagedType)
            {
                return memberType.SpecialType != SpecialType.None ? MemberKind.BuiltIn : MemberKind.Unmanaged;
            }
            else if (memberType.TypeKind == TypeKind.Array)
            {
                if (memberType is IArrayTypeSymbol array)
                {
                    if (array.IsSZArray)
                        return MemberKind.Array;
                }
            }
            else if (!memberType.IsUnmanagedType)
            {
                if (memberType.TypeKind == TypeKind.Struct)
                {
                    return MemberKind.Struct;
                }
                else if (memberType is INamedTypeSymbol namedType && namedType.IsGenericType)
                {
                    var typeArguments = namedType.TypeArguments;
                    var genericTypeName = namedType.Name;

                    if (genericTypeName == "List" && typeArguments.Length == 1)
                    {
                        return MemberKind.List;
                    }
                    else if (genericTypeName == "Dictionary" && typeArguments.Length == 2)
                    {
                        return MemberKind.Dictionary;
                    } 
                }
                else if (memberType.TypeKind == TypeKind.Class)
                {
                    return MemberKind.Class;
                }
                else if (memberType.TypeKind == TypeKind.Interface)
                {
                    return MemberKind.Interface;
                }
            }
            return MemberKind.NonSerializable;
        }
    }
}

